package utils

import (
	"crypto/md5"
	"errors"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/gin-gonic/gin"
	"github.com/gomarkdown/markdown"
	. "github.com/hunterhug/go_image"
	"gopkg.in/ini.v1"
	"io"
	"math/rand"
	"mime/multipart"
	"os"
	"path"
	"reflect"
	"strconv"
	"strings"
	"time"
	. "xm_shop/database"
	. "xm_shop/models"
)

func Md5(str string) string {
	h := md5.New()                       // 创建md5对象
	io.WriteString(h, str)               // 将字符串写入h中
	return fmt.Sprintf("%x", h.Sum(nil)) //h.Sum()对字符串进行md5加密
	// h.Sum(nil) 返回一个字节数据, fmt.Sprintf("%x", h.Sum(nil))将字节数据转换为16进制字符串, 并返回
}

// 上传图片到oss
func OssUploadImg(context *gin.Context, picName string) (string, error) {
	// 1. 获取上传的文件
	file, err1 := context.FormFile(picName)
	if err1 != nil {
		return "", err1
	}
	// 获取文件后缀
	extName := path.Ext(file.Filename)
	// 创建合法文件后缀map
	allowExtMap := map[string]bool{
		".png":  true,
		".jpg":  true,
		".jpeg": true,
		".gif":  true,
	}
	if _, ok := allowExtMap[extName]; !ok {
		return "", errors.New("文件后缀不合法")
	}
	// 指定文件保存目录
	day := time.Now().Format("20060102")
	dir := "static/upload/" + day // oss的保存文件目录
	// 生成文件名
	fileName := strconv.FormatInt(time.Now().UnixNano(), 10) + extName
	// 文件保存路径
	dst := path.Join(dir, fileName)
	// 执行oss文件上传
	src, err := OssUpload(file, dst)
	if err != nil {
		return "", err
	}
	return src, nil
}

// 封装oss上传方法
func OssUpload(file *multipart.FileHeader, dst string) (string, error) {
	// 上传文件流
	// 创建OSSClient实例。
	// yourEndpoint填写Bucket对应的Endpoint，以华东1（杭州）为例，填写为https://oss-cn-hangzhou.aliyuncs.com。其它Region请按实际情况填写。
	client, err := oss.New("https://oss-cn-beijing.aliyuncs.com", "LTAI5t8ZQxomaSfkba9oYqFu", "ARClSwWWUGkfI2iKCVmjaxFq0iTBQA")
	if err != nil {
		return "", err
	}

	// 获取存储桶空间(已创建的)
	bucket, err := client.Bucket("xm-shopping")
	if err != nil {
		return "", err
	}

	// 打开文件流
	src, err := file.Open()
	if err != nil {
		return "", err
	}
	defer src.Close()

	// 将文件流上传至exampledir目录下的exampleobject.txt文件。
	err = bucket.PutObject(dst, src)
	if err != nil {
		return "", err
	}
	return dst, nil
}

// 使用反射获取属性值  ColumnName 为结构体的属性名
func GetSettingFromColumn(ColumnName string) string {
	var setting Setting
	DB.Take(&setting)
	// 通过反射获取
	v := reflect.ValueOf(setting)
	val := v.FieldByName(ColumnName).String() // 获取到对应字段值的字符串形式
	return val
}

// 获取oss的状态
func GetOssStatus() int {
	// 读取配置文件
	config, iniErr := ini.Load("./conf/app.ini")
	if iniErr != nil {
		fmt.Printf("fail to read file %v", iniErr)
		os.Exit(1)
	}
	status, err := config.Section("oss").Key("status").Int()
	if err != nil {
		fmt.Println("oss状态读取失败")
		os.Exit(1)
	}
	return status
}

// 生成商品缩略图
func ResizeGoodsImage(fileName string) {
	// 获取文件后缀名
	extName := path.Ext(fileName) // fileName其实是一个文件路径 static/upload/aaa.jpg
	// 获取尺寸字段  同时实行中文转换防止，
	thumbnailSize := strings.ReplaceAll(GetSettingFromColumn("ThumbnailSize"), "，", ",")
	// 转成切片
	thumbnailSizeSlice := strings.Split(thumbnailSize, ",")
	for i := 0; i < len(thumbnailSizeSlice); i++ {
		// 创建文件保存路径
		savePath := fileName + "_" + thumbnailSizeSlice[i] + "x" + thumbnailSizeSlice[i] + extName
		w, _ := strconv.Atoi(thumbnailSizeSlice[i])
		// 对图片做裁剪
		err := ThumbnailF2F(fileName, savePath, w, w)
		if err != nil {
			fmt.Println(err) // TODO 配置成日志
		}
	}
}

// 将markdown语法转html
func FormatAttr(str string) string { // str传入markdown语法的数据
	//md := []byte("## 我是一个二级标题")
	var tempStr string
	tempSlice := strings.Split(str, "\n") // 每个回车后面都跟着一个隐藏的换行符\n
	for _, v := range tempSlice {
		md := []byte(v)
		output := markdown.ToHTML(md, nil, nil) // markdown每一句转换成html文本的时候, 都会带上换行的效果: p标签
		tempStr += string(output)
	}
	return tempStr
}

// 生成随机数
func GetRandomNum() string {
	var str string
	for i := 0; i < 4; i++ {
		current := rand.Intn(10) // 生成0 - 9 之间的随机数
		str += strconv.Itoa(current)
	}
	return str
}

// 生成订单号
func GetOrderNo() string {
	return time.Now().Format("20060102150405") + GetRandomNum()
}
